/**
 *
 */
package com.gxa.ehome.dao;

import com.gxa.ehome.util.DBUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * 所有的DAO层操作的通用类DAO
 *
 * @author bilei
 * @date 2021年4月1日
 */
public class BaseDao<T> {

    // 引入DBUtil
    private DBUtil dbutil = new DBUtil();

    // 子类调用的时候告诉BaseDao具体的Class类型
    /*
     * public T selectById(Integer id, Class<T> clazz) throws Exception {
     *
     * // 数据表的名称各不相同, 必须通过Class体现 // 1. 通过clazz获取类的简单名称 String simpleName =
     * clazz.getSimpleName(); // User/Fund String sql = "SELECT * FROM tb_" +
     * simpleName + " WHERE " + simpleName + "_id=?";
     *
     * // 将sql预编译 PreparedStatement preparedStatement =
     * dbutil.getPreparedStatement(sql); // 传参, 给id赋值 preparedStatement.setObject(1,
     * id); // 生成结果集对象 ResultSet resultSet = dbutil.execQuery(preparedStatement);
     *
     * // 创建一个目标对象(User/Fund) T t = null; // 利用泛型类型声明,不能实例化
     *
     * // 游标滚动,找到数据 if (resultSet.next()) { // 给目标对象实例化, 结合反射利用Constructor实例化 //
     * 设计实体类的时候(Fund/User), 保证含有一个空参构造 // getConstructor()一定能够顺利获取构造方法,并实例化 //
     * clazz.newInstance(); 在1.9的时候过时 t = clazz.getConstructor().newInstance();
     *
     * // 给字段赋值: 必须通过反射方式 Field[] fields = clazz.getDeclaredFields(); // 遍历每一个Field
     * for (Field field : fields) { // 因为字段都是private, 先获取操作权限
     * field.setAccessible(true); field.set(t, resultSet.getObject(simpleName + "_"
     * + field.getName())); // field.getName() } } dbutil.closeAll(); return t; }
     */

    // 使用Method方式执行反射操作
    public T selectById(Integer id, Class<T> clazz) throws Exception {

        // 数据表的名称各不相同, 必须通过Class体现
        // 1. 通过clazz获取类的简单名称
        String simpleName = clazz.getSimpleName(); // User/Fund
        String sql = "SELECT * FROM tb_" + simpleName + " WHERE " + simpleName + "_id=?";

        // 将sql预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        // 传参, 给id赋值
        preparedStatement.setObject(1, id);
        // 生成结果集对象
        ResultSet resultSet = dbutil.execQuery(preparedStatement);

        // 创建一个目标对象(User/Fund)
        T t = null; // 利用泛型类型声明,不能实例化

        // 游标滚动,找到数据
        if (resultSet.next()) {
            t = clazz.getConstructor().newInstance();
            Field[] fields = clazz.getDeclaredFields();
            // 遍历每一个Field
            for (Field field : fields) {
                // 因为字段都是private, 先获取操作权限
                //field.setAccessible(true);
                //field.set(t, resultSet.getObject(simpleName + "_" + field.getName())); // field.getName()

                //System.out.println(field.getName());  // id


                String fieldName = field.getName();  // id
                if (fieldName.startsWith("is")) {
                    // 单独把is去掉: setVip
                } else {
                    // Id
//                    fieldName = fieldName.replace(fieldName.charAt(0), Character.toUpperCase(fieldName.charAt(0)));
//                   用上面的替换方法会使所有跟首字母一样的字符都会改变,从而使的有些方法拿不到
                    fieldName=fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);

                    // setId
                    fieldName = "set" + fieldName;

                    // 生成一个Method对象, 声明参数Class的时候,如果用Object作为通用类型,会抛出异常 NoSuchMethod
                    // 必须类型匹配
                    Method method = clazz.getDeclaredMethod(fieldName, field.getType());

                    // 反射执行setter
                    // user_id
                    String columnName = simpleName + "_" + field.getName();
                    method.invoke(t, resultSet.getObject(columnName));
                }

            }
        }
        dbutil.closeAll();
        return t;
    }

    public Integer deleteById(Integer id, Class<T> clazz) throws Exception {
        // 通过t对象获取Class对象
        String simpleName = clazz.getSimpleName();
        String sql = "DELETE FROM tb_" + simpleName + " WHERE " + simpleName + "_id=?";
        //执行预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        preparedStatement.setObject(1, id);
        int i = dbutil.execUpdate(preparedStatement);
        dbutil.closeAll();
        return i;
    }

    /**
     * 通用的添加方法
     * @param t 泛型的目标对象
     * @return 影响的行数
     * @throws Exception
     */
    public Integer insert(T t) throws Exception {
        // String insert = "INSERT INTO tb_user VALUES(NULL,?,?,?)";

        // 通过t对象获取Class对象
        Class<?> clazz = t.getClass();
        String simpleName = clazz.getSimpleName();
        // 反射得到所有的字段数组
        Field[] fields = clazz.getDeclaredFields(); // 包含id

        //准备一个StringBuilder操作字符串的拼接
        StringBuilder builder = new StringBuilder("INSERT INTO tb_");
        builder.append(simpleName);
        builder.append(" VALUES(NULL");

        //利用遍历,动态获取?的数量
        for (int i = 1; i < fields.length; i++) {
            builder.append(",?");
        }
        //最末尾的括号
        builder.append(")");

        //执行预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(builder.toString());

        //通过遍历Fields,给预编译语句中的参数赋值
        for (int i = 1; i < fields.length; i++) {
            fields[i].setAccessible(true);
            preparedStatement.setObject(i, fields[i].get(t));
        }
        int lines = preparedStatement.executeUpdate();
        dbutil.closeAll();
        return lines;
    }

    /**
     * 通用的修改方法
     * @param id 目标对象的编号
     * @param t 目标对象
     * @throws Exception
     */
    public void update(Integer id, T t) throws Exception {

        Class<?> clazz = t.getClass();
        String simpleName = clazz.getSimpleName();

        StringBuilder sql = new StringBuilder("UPDATE tb_" + simpleName + " SET ");

        // 反射得到所有的字段数组
        Field[] fields = clazz.getDeclaredFields(); // 包含id

        //通过遍历Fields:  UPDATE tb_User SET name=?,pwd=?,username=?
        for (int i = 1; i < fields.length; i++) {
            sql.append(simpleName + "_" + fields[i].getName() + "=?,");
        }

        // 消除最后一个多余的逗号
        sql.deleteCharAt(sql.length() - 1);
        System.out.println(sql);

        sql.append(" WHERE " + simpleName + "_id=?");

        //执行预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql.toString());

        // 参数注入值
        //给? 占位符赋值
        int i;
        for (i = 1; i < fields.length; i++) {
            String fieldName = fields[i].getName();  //  name/pwd/username
            System.out.println(fieldName);
            if (fieldName.startsWith("is")) {
                // 单独把is去掉: setVip
            } else {
                // Name
//                fieldName = fieldName.replace(fieldName.charAt(0), Character.toUpperCase(fieldName.charAt(0)));
                fieldName=fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
                // getName
                fieldName = "get" + fieldName;

                // 生成一个Method对象, 声明参数Class的时候,如果用Object作为通用类型,会抛出异常 NoSuchMethod
                // 必须类型匹配 getName()
                Method method = clazz.getDeclaredMethod(fieldName);

                // 反射执行getter
                // name: getName()
                preparedStatement.setObject(i, method.invoke(t));
            }
        }
        // 把id直接作为最后一个?对应的条件
        preparedStatement.setObject(i, id);
        int line = dbutil.execUpdate(preparedStatement);
        dbutil.closeAll();
    }

    public Integer update1(Integer id, T t) throws Exception {

        Class<?> clazz = t.getClass();
        String simpleName = clazz.getSimpleName();

        StringBuilder sql = new StringBuilder("UPDATE tb_" + simpleName + " SET ");

        // 反射得到所有的字段数组
        Field[] fields = clazz.getDeclaredFields(); // 包含id

        //通过遍历Fields:  UPDATE tb_User SET name=?,pwd=?,username=?
        for (int i = 1; i < fields.length; i++) {
            sql.append(simpleName + "_" + fields[i].getName() + "=?,");
        }

        // 消除最后一个多余的逗号
        sql.deleteCharAt(sql.length() - 1);
        System.out.println(sql);

        sql.append(" WHERE " + simpleName + "_id=?");

        //执行预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql.toString());

        // 参数注入值
        //给? 占位符赋值
        int i;
        for (i = 1; i < fields.length; i++) {
            String fieldName = fields[i].getName();  //  name/pwd/username
            System.out.println(fieldName);
            // Name
            fieldName = fieldName.replace(fieldName.charAt(0), Character.toUpperCase(fieldName.charAt(0)));

            if (fieldName.startsWith("In")) {
                fieldName = "is" + fieldName;
            } else {
                fieldName = "get" + fieldName;
            }
            // getName


            // 生成一个Method对象, 声明参数Class的时候,如果用Object作为通用类型,会抛出异常 NoSuchMethod
            // 必须类型匹配 getName()
            Method method = clazz.getDeclaredMethod(fieldName);

            // 反射执行getter
            // name: getName()
            preparedStatement.setObject(i, method.invoke(t));

        }
        // 把id直接作为最后一个?对应的条件
        preparedStatement.setObject(i, id);
        int line = dbutil.execUpdate(preparedStatement);
        dbutil.closeAll();
        return line;
    }
    /*
     * public User selectByName(String name) throws Exception ;
     * public List<User> selectAll();
     */

    public T selectByUserName(String username, Class<T> clazz) throws Exception {

        String simpleName = clazz.getSimpleName(); // User

        String sql = "SELECT * FROM tb_" + simpleName + " WHERE " + simpleName + "_username=?";

        // 将sql预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        // 传参, 给id赋值
        preparedStatement.setObject(1, username);
        // 生成结果集对象
        ResultSet resultSet = dbutil.execQuery(preparedStatement);

        // 创建一个目标对象(User/Fund)
        T t = null; // 利用泛型类型声明,不能实例化

        // 游标滚动,找到数据
        if (resultSet.next()) {
            t = clazz.getConstructor().newInstance();
            Field[] fields = clazz.getDeclaredFields();
            // 遍历每一个Field
            for (Field field : fields) {


                String fieldName = field.getName();  // username
                if (fieldName.startsWith("is")) {
                    // 单独把is去掉: setVip
                } else {
                    // Username
                    fieldName = fieldName.replace(fieldName.charAt(0), Character.toUpperCase(fieldName.charAt(0)));

                    // setUsername
                    fieldName = "set" + fieldName;

                    // 生成一个Method对象, 声明参数Class的时候,如果用Object作为通用类型,会抛出异常 NoSuchMethod
                    // 必须类型匹配
                    Method method = clazz.getDeclaredMethod(fieldName, field.getType());

                    // 反射执行setter
                    // user_username
                    String columnName = simpleName + "_" + field.getName();
                    method.invoke(t, resultSet.getObject(columnName));
                }

            }
        }
        dbutil.closeAll();
        return t;
    }


    public List<T> selectByPage(int page, int size, Class<T> clazz) throws Exception {

        // 声明一个用于返回的数据集合
        List<T> list = new ArrayList<>();

        String simpleName = clazz.getSimpleName(); // User

//        String sql = "SELECT * FROM tb_" + simpleName + " limit ?,?";
        String sql = "SELECT * FROM tb_" + simpleName + " ORDER BY " + simpleName + "_id DESC LIMIT ?,?";

        // 将sql预编译
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        // 传参, 给page赋值
        preparedStatement.setObject(1, (page - 1) * size);
        // 传参, 给size赋值
        preparedStatement.setObject(2, size);
        // 生成结果集对象
        ResultSet resultSet = dbutil.execQuery(preparedStatement);

        while (resultSet.next()) {
            // 每得到一条数据就产生一个T对象
            T t = clazz.getConstructor().newInstance();
            // 获取了反射后的字段列表
            Field[] declaredFields = clazz.getDeclaredFields();
// 遍历每一个Field
            for (Field field : declaredFields) {
                String fieldName = field.getName();  // id/username/name/pwd
                if (fieldName.startsWith("is")) {
                    // 单独把is去掉: setVip
                } else {
                    // Username
                    fieldName = fieldName.replace(fieldName.charAt(0), Character.toUpperCase(fieldName.charAt(0)));

                    // setUsername
                    fieldName = "set" + fieldName;

                    // 生成一个Method对象, 声明参数Class的时候,如果用Object作为通用类型,会抛出异常 NoSuchMethod
                    // 必须类型匹配
                    Method method = clazz.getDeclaredMethod(fieldName, field.getType());

                    // 反射执行setter
                    // user_username
                    String columnName = simpleName + "_" + field.getName();
                    method.invoke(t, resultSet.getObject(columnName));
                }

            }
            list.add(t);
        }
        return list;
    }

    public List<T> selectAll(Class<T> clazz) throws Exception {
        String simpleName = clazz.getSimpleName();
        String sql = "SELECT * FROM tb_" + simpleName;

        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        ResultSet resultSet = dbutil.execQuery(preparedStatement);

        List<T> list = new ArrayList<T>();
        T t = null;
        while (resultSet.next()) {
            t = clazz.getConstructor().newInstance();

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                field.set(t, resultSet.getObject(simpleName + "_" + field.getName()));
            }
            list.add(t);

        }
        dbutil.closeAll();
        return list;
    }
    public List<T> selectLikeNameorPhoneByPage(String searchString, int page, int size, Class<T> clazz) throws Exception {
        String simpleName = clazz.getSimpleName();
        String sql = "SELECT * FROM tb_" + simpleName + " WHERE " + simpleName + "_cpname LIKE ? OR " + simpleName + "_phone LIKE ?" + " LIMIT ?,?";
        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        preparedStatement.setString(1, "%" + searchString + "%");
        preparedStatement.setString(2, "%" + searchString + "%");
        preparedStatement.setInt(3, (page - 1) * size);
        preparedStatement.setInt(4, size);

        ResultSet resultSet = dbutil.execQuery(preparedStatement);

        List<T> list = new ArrayList<T>();
        T t = null;
        while (resultSet.next()) {
            t = clazz.getConstructor().newInstance();

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                field.set(t, resultSet.getObject(simpleName + "_" + field.getName()));
            }
            list.add(t);
        }
        dbutil.closeAll();
        return list;
    }

    public Integer getCount(Class<T> clazz) throws Exception {
        int count = 0;
        String simpleName = clazz.getSimpleName();
        String sql = "SELECT COUNT(*) FROM tb_" + simpleName;

        PreparedStatement preparedStatement = dbutil.getPreparedStatement(sql);
        ResultSet resultSet = dbutil.execQuery(preparedStatement);
        if (resultSet.next()) {
            count = resultSet.getInt(1);
        }
        dbutil.closeAll();
        return count;
    }
}
